//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <aura.h>
#include "private.h"
#include "wince.h"
#include <stdio.h>

typedef struct _IpcChannelDescr {
    HANDLE hReadMsgQ;
    HANDLE hWriteMsgQ;
}IpcChannelDescr;


AURA_API AuraIpcChannel_Create(
        /* [in] */ Aura_PWChar name,
        /* [out] */ Aura_ChannelId *pChannelId)
{
    IpcChannelDescr * pDescr = NULL;
    MSGQUEUEOPTIONS msgQopt;
    Aura_ECode ec = AURA_NOERROR;
    wchar_t tempBuf[64];

    pDescr = (IpcChannelDescr *)Aura_malloc(sizeof(_IpcChannelDescr));
    if (NULL == pDescr) {
        return AURA_E_OUT_OF_MEMORY;
    }

    memset(&msgQopt, 0, sizeof(msgQopt));
    msgQopt.dwSize = sizeof(msgQopt);
    msgQopt.dwFlags = MSGQUEUE_ALLOW_BROKEN;
    msgQopt.dwMaxMessages = 5;
    msgQopt.cbMaxMessage = 128;

    msgQopt.bReadAccess = TRUE;
    wcscpy(tempBuf, (const wchar_t *)name);
    pDescr->hReadMsgQ = CreateMsgQueue((LPCWSTR)tempBuf, &msgQopt);
    if (NULL == pDescr->hReadMsgQ) {
        goto E_FAIL_EXIT;
    }

    msgQopt.bReadAccess = FALSE;
    wcscat(tempBuf, L".WriteMsgQ");
    pDescr->hWriteMsgQ = CreateMsgQueue((LPCWSTR)tempBuf, &msgQopt);
    if (NULL == pDescr->hWriteMsgQ) {
        goto E_FAIL_EXIT;
    }

    *pChannelId = (Aura_ChannelId)pDescr;

    return AURA_NOERROR;

E_FAIL_EXIT:
    ec = GetLastECodeError();
    if (NULL != pDescr->hReadMsgQ) {
        CloseMsgQueue(pDescr->hReadMsgQ);
    }
    if (NULL != pDescr->hWriteMsgQ) {
        CloseMsgQueue(pDescr->hWriteMsgQ);
    }
    Aura_free(pDescr);
    return ec;
}

AURA_API AuraIpcChannel_Destroy(
        /* [in] */ Aura_ChannelId channelId)
{
    IpcChannelDescr* pDescr = (IpcChannelDescr *)channelId;

    assert(NULL != pDescr);
    assert(NULL != pDescr->hReadMsgQ);
    assert(NULL != pDescr->hWriteMsgQ);

    CloseMsgQueue(pDescr->hReadMsgQ);
    CloseMsgQueue(pDescr->hWriteMsgQ);

    Aura_free(pDescr);

    return AURA_NOERROR;
}

AURA_API AuraIpcChannel_WaitForConnection(
        /* [in] */ Aura_PWChar channelName,
        /* [out] */ Aura_ChannelId * pListenedId,
        /* [out] */ Aura_ChannelId * pChannelId)
{
    IpcChannelDescr * pSrvDescr  = (IpcChannelDescr *)*pListenedId;
    IpcChannelDescr * pDataDescr = NULL;
    MSGQUEUEOPTIONS msgQopt;
    wchar_t tempBuf[64];
    Aura_ECode ec = AURA_NOERROR;

    pDataDescr = (IpcChannelDescr *)Aura_malloc(sizeof(IpcChannelDescr));
    if (NULL == pDataDescr) {
        return AURA_E_OUT_OF_MEMORY;
    }
    memset(pDataDescr, 0, sizeof(IpcChannelDescr));

    {
        DWORD bytesRead, dwFlags;
        BOOL bSuccess = FALSE;
        bSuccess = ReadMsgQueue(pSrvDescr->hReadMsgQ,
                        (LPVOID)tempBuf, 2 * 64,
                        &bytesRead,
                        INFINITE,
                        &dwFlags
                   );
        if (!bSuccess) {
            ec = GetLastECodeError();
            goto E_FAIL_EXIT;
        }
    }

    memset(&msgQopt, 0, sizeof(msgQopt));
    msgQopt.dwSize = sizeof(msgQopt);
    msgQopt.dwFlags = 0;
    msgQopt.dwMaxMessages = 0;
    msgQopt.cbMaxMessage = 10 * 1024;

    msgQopt.bReadAccess = FALSE;
    pDataDescr->hWriteMsgQ = CreateMsgQueue((LPCWSTR)tempBuf, &msgQopt);
    if (NULL == pDataDescr->hWriteMsgQ) {
        ec = GetLastECodeError();
        goto E_FAIL_EXIT;
    }
    else {
        if (GetLastError() != ERROR_ALREADY_EXISTS) {
            ec = AURA_E_DOES_NOT_EXIST;
            goto E_FAIL_EXIT;
        }
    }

    wcscat(tempBuf, L".Write");
    msgQopt.bReadAccess = TRUE;
    pDataDescr->hReadMsgQ = CreateMsgQueue((LPCWSTR)tempBuf, &msgQopt);
    if (NULL == pDataDescr->hReadMsgQ) {
        ec = GetLastECodeError();
        goto E_FAIL_EXIT;
    }
    else {
        if (GetLastError() != ERROR_ALREADY_EXISTS) {
            ec = AURA_E_DOES_NOT_EXIST;
            goto E_FAIL_EXIT;
        }
    }

    {
        BOOL bSuccess = FALSE;
        bSuccess = WriteMsgQueue(pSrvDescr->hWriteMsgQ,
                         (LPVOID)tempBuf, 4,
                         INFINITE,
                         0);
        if (!bSuccess) {
            ec = GetLastECodeError();
            goto E_FAIL_EXIT;
        }
    }

    *pChannelId = (Aura_ChannelId)pDataDescr;
    return AURA_NOERROR;

E_FAIL_EXIT:
    if (NULL != pDataDescr->hReadMsgQ) {
        CloseMsgQueue(pDataDescr->hReadMsgQ);
    }
    if (NULL != pDataDescr->hWriteMsgQ) {
        CloseMsgQueue(pDataDescr->hWriteMsgQ);
    }
    Aura_free(pDataDescr);

    return ec;
}

AURA_API AuraIpcChannel_Read(
        /* [in] */ Aura_ChannelId channelId,
        /* [in] */ Aura_PVoid pBuffer,
        /* [in] */ Aura_UInt32 bufferSize)
{
    IpcChannelDescr * pDescr = (IpcChannelDescr *)channelId;
    BOOL bSuccess = FALSE;
    DWORD bytesRead, dwFlags;
    Aura_ECode ec = AURA_NOERROR;

    assert(NULL != pDescr);
    assert(NULL != pDescr->hReadMsgQ);

    bSuccess = ReadMsgQueue(pDescr->hReadMsgQ,
                    pBuffer, bufferSize,
                    &bytesRead,
                    INFINITE,
                    &dwFlags
               );
    if (!bSuccess) {
        DWORD dwErr = GetLastError();
        if (dwErr == ERROR_INVALID_HANDLE) {
             ec = AURA_E_PROCESS_ALREADY_EXITED;
        }
        else {
             ec = Win32ErrorToECodeError(dwErr);
        }
        goto E_FAIL_EXIT;
    }

    return AURA_NOERROR;

E_FAIL_EXIT:
    return ec;
}

AURA_API AuraIpcChannel_AsyncWrite(
        /* [in] */ Aura_ChannelId channelId,
        /* [in] */ Aura_PVoid pBuffer,
        /* [in] */ Aura_UInt32 bufferSize)
{
    IpcChannelDescr * pDescr = (IpcChannelDescr *)channelId;
    BOOL bSuccess = FALSE;

    assert(NULL != pDescr);
    assert(NULL != pDescr->hWriteMsgQ);

    bSuccess = WriteMsgQueue(pDescr->hWriteMsgQ,
                     pBuffer, bufferSize,
                     INFINITE,
                     0
               );
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }
    return AURA_NOERROR;

E_FAIL_EXIT:
    return GetLastECodeError();
}

AURA_API AuraIpcChannel_SyncWrite(
        /* [in] */ Aura_ChannelId channelId,
        /* [in] */ Aura_PVoid pInBuffer,
        /* [in] */ Aura_UInt32 inBufferSize,
        /* [out] */ Aura_PVoid pOutBuffer,
        /* [in] */ Aura_UInt32 outBuferSize)
{
    IpcChannelDescr * pDescr = (IpcChannelDescr *)channelId;
    BOOL bSuccess = FALSE;
    DWORD bytesRead, dwFlags;
    Aura_ECode ec = AURA_NOERROR;

    assert(NULL != pDescr);
    assert(NULL != pDescr->hReadMsgQ);
    assert(NULL != pDescr->hWriteMsgQ);

    bSuccess = WriteMsgQueue(pDescr->hWriteMsgQ,
                     pInBuffer, inBufferSize,
                     INFINITE,
                     0
               );
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }
    bSuccess = ReadMsgQueue(pDescr->hReadMsgQ,
                    pOutBuffer, outBuferSize,
                    &bytesRead,
                    INFINITE,
                    &dwFlags);
    if (!bSuccess) {
        goto E_FAIL_EXIT;
    }
    return AURA_NOERROR;

E_FAIL_EXIT:
    {
        DWORD dwErr = GetLastError();
        if (dwErr == ERROR_INVALID_HANDLE) {
             ec = AURA_E_PROCESS_ALREADY_EXITED;
        }
        else {
             ec = Win32ErrorToECodeError(dwErr);
        }
    }
    return ec;
}

STATIC void ConstructMsgQName(wchar_t * pName, int maxSize)
{
     STATIC DWORD processId = 0;
     STATIC DWORD times = 0;

     if (processId == 0) {
         processId = GetCurrentProcessId();
     }
     times++;

     wcscpy(pName, L"AuraMsgQ.");
     int len = wcslen(pName);
     len += swprintf(pName + len, L"%ud.", processId);
     len += swprintf(pName + len, L"%d" , times);

     assert(len < maxSize);

     return ;
}

AURA_API AuraIpcChannel_Connect(
        /* [in] */ Aura_PWChar channelName,
        /* [out] */ Aura_ChannelId * pChannelId)
{
    HANDLE hSrvReadMsgQ  = NULL;
    HANDLE hSrvWriteMsgQ = NULL;
    IpcChannelDescr * pDescr = NULL;
    MSGQUEUEOPTIONS msgQopt;
    wchar_t tempBuf[64];
    wchar_t msg[64];
    BOOL bSuccess = FALSE;
    DWORD bytesRead, dwFlags;
    Aura_ECode ec = AURA_NOERROR;
    DWORD err = 0;

    assert(NULL != pChannelId);

    pDescr = (IpcChannelDescr *)Aura_malloc(sizeof(IpcChannelDescr));
    if (NULL == pDescr) {
        return AURA_E_OUT_OF_MEMORY;
    }

    memset(&msgQopt, 0, sizeof(msgQopt));
    msgQopt.dwSize = sizeof(msgQopt);
    msgQopt.dwFlags = MSGQUEUE_ALLOW_BROKEN;
    msgQopt.dwMaxMessages = 5;
    msgQopt.cbMaxMessage = 128;

    msgQopt.bReadAccess = FALSE;
    wcscpy(tempBuf, (const wchar_t *)channelName);
    hSrvWriteMsgQ = CreateMsgQueue((LPCWSTR)tempBuf, &msgQopt);
    if (NULL == hSrvWriteMsgQ) {
        ec = GetLastECodeError();
        goto E_FAIL_EXIT;
    }
    else {
        if (GetLastError() != ERROR_ALREADY_EXISTS) {
            ec = AURA_E_DOES_NOT_EXIST;
            goto E_FAIL_EXIT;
        }
    }

    msgQopt.bReadAccess = TRUE;
    wcscat(tempBuf, L".WriteMsgQ");
    hSrvReadMsgQ = CreateMsgQueue((LPCWSTR)tempBuf, &msgQopt);
    if (NULL == hSrvReadMsgQ) {
        ec = GetLastECodeError();
        goto E_FAIL_EXIT;
    }
    else {
        if (GetLastError() != ERROR_ALREADY_EXISTS) {
            ec = AURA_E_DOES_NOT_EXIST;
            goto E_FAIL_EXIT;
        }
    }

    ConstructMsgQName(tempBuf, 64);
    wcscpy(msg, tempBuf);
    msgQopt.dwFlags = 0;
    msgQopt.dwMaxMessages = 0;
    msgQopt.cbMaxMessage = 10 * 1024;

    msgQopt.bReadAccess = TRUE;
    pDescr->hReadMsgQ = CreateMsgQueue((LPCWSTR)tempBuf, &msgQopt);
    if (NULL == pDescr->hReadMsgQ) {
        ec = GetLastECodeError();
        goto E_FAIL_EXIT;
    }
    msgQopt.bReadAccess = FALSE;
    wcscat(tempBuf, L".Write");
    pDescr->hWriteMsgQ = CreateMsgQueue((LPCWSTR)tempBuf, &msgQopt);
    if (NULL == pDescr->hWriteMsgQ) {
        ec = GetLastECodeError();
        goto E_FAIL_EXIT;
    }

    bSuccess = WriteMsgQueue(hSrvWriteMsgQ,
                     (LPVOID)msg, 2*64,
                     INFINITE,
                     0
               );
    if (!bSuccess) {
        ec = GetLastECodeError();
        goto E_FAIL_EXIT;
    }
    bSuccess = ReadMsgQueue(hSrvReadMsgQ,
                     (LPVOID)msg, 2*64,
                     &bytesRead,
                     INFINITE,
                     &dwFlags
               );
    if (!bSuccess) {
        ec = GetLastECodeError();
        goto E_FAIL_EXIT;
    }

    CloseMsgQueue(hSrvReadMsgQ);
    CloseMsgQueue(hSrvWriteMsgQ);

    *pChannelId = (Aura_ChannelId)pDescr;
    return AURA_NOERROR;

E_FAIL_EXIT:
    if (NULL != hSrvReadMsgQ) {
        CloseMsgQueue(hSrvReadMsgQ);
    }
    if (NULL != hSrvWriteMsgQ) {
        CloseMsgQueue(hSrvWriteMsgQ);
    }
    if (NULL != pDescr->hReadMsgQ) {
        CloseMsgQueue(pDescr->hReadMsgQ);
    }
    if (NULL != pDescr->hWriteMsgQ) {
        CloseMsgQueue(pDescr->hWriteMsgQ);
    }
    Aura_free(pDescr);

    return ec;
}


AURA_API AuraIpcChannel_Disconnect(
        /* [in] */ Aura_ChannelId channelId)
{
    IpcChannelDescr* pDescr = (IpcChannelDescr *)channelId;

    assert(NULL != pDescr);
    assert(NULL != pDescr->hReadMsgQ);
    assert(NULL != pDescr->hWriteMsgQ);

    CloseMsgQueue(pDescr->hReadMsgQ);
    CloseMsgQueue(pDescr->hWriteMsgQ);

    Aura_free(pDescr);

    return AURA_NOERROR;
}

